<!doctype html>
<html xml:lang="zh-CN" lang="zh-CN">

<head>
        <link rel="canonical" href="https://v2rayjichang.github.io/news/article-100331.htm" />
    <!--====== Required meta tags ======-->
    <meta charset="utf-8">
    <meta http-equiv="x-ua-compatible" content="ie=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <title>JavaWeb项目基于SpringBoot框架实现统一的数据返回格式 + 统一异常处理 + 统一日志处理</title>
        <meta name="description" content="目录 ????前言 ????统一返回格式 &nbsp;添加枚举类 &nbsp;添加统一返回格式的类 &nbsp;测试 ????统一异常处理 &nbsp;添加统一异常处理器（创建一个handler包，在" />
        <link rel="icon" href="/assets/website/img/surfboardnode/favicon.ico" type="image/x-icon"/>

    <meta name="author" content="V2ray免费机场 节点订阅中文官网">
    <meta property="og:type" content="article" />
    <meta property="og:url" content="https://v2rayjichang.github.io/news/article-100331.htm" />
    <meta property="og:site_name" content="V2ray免费机场 节点订阅中文官网" />
    <meta property="og:title" content="JavaWeb项目基于SpringBoot框架实现统一的数据返回格式 + 统一异常处理 + 统一日志处理" />
    <meta property="og:image" content="https://v2rayjichang.github.io/uploads/20240709-1/8c0be97f61bea1669597e3178d0e6d0e.webp" />
        <meta property="og:release_date" content="2025-04-20T09:04:42" />
    <meta property="og:updated_time" content="2025-04-20T09:04:42" />
        <meta property="og:description" content="目录 ????前言 ????统一返回格式 &nbsp;添加枚举类 &nbsp;添加统一返回格式的类 &nbsp;测试 ????统一异常处理 &nbsp;添加统一异常处理器（创建一个handler包，在" />
        
    <meta name="applicable-device" content="pc,mobile" />
    <meta name="renderer" content="webkit" />
    <meta name="force-rendering" content="webkit" />
    <meta http-equiv="Cache-Control" content="no-transform" />
    <meta name="robots" content="max-image-preview:large" />
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="black">
    <meta name="apple-mobile-web-app-title" content="JavaWeb项目基于SpringBoot框架实现统一的数据返回格式 + 统一异常处理 + 统一日志处理">
    <meta name="format-detection" content="telephone=no">

    <link rel="dns-prefetch" href="https:/www.googletagmanager.com">
    <link rel="dns-prefetch" href="https://www.googleadservices.com">
    <link rel="dns-prefetch" href="https://www.google-analytics.com">
    <link rel="dns-prefetch" href="https://pagead2.googlesyndication.com">
    <link rel="dns-prefetch" href="https://cm.g.doubleclick.net">
    
    <!--====== Bootstrap css ======-->
    <link rel="stylesheet" href="/assets/website/css/surfboardnode/bootstrap.min.css">
    <!--====== Slick css ======-->
    <link rel="stylesheet" href="/assets/website/css/surfboardnode/slick.css">
    <!--====== Magnific Popup css ======-->
    <link rel="stylesheet" href="/assets/website/css/surfboardnode/magnific-popup.css">
    <!--====== Line Icons css ======-->
    <link rel="stylesheet" href="/assets/website/css/surfboardnode/LineIcons.css">
    <!--====== Default css ======-->
    <link rel="stylesheet" href="/assets/website/css/surfboardnode/default.css">
    <!--====== Style css ======-->
    <link rel="stylesheet" href="/assets/website/css/surfboardnode/style.css">
    <link rel="stylesheet" href="/assets/website/css/G.css" />
    <!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-LMZ4WN7LCM"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());

  gtag('config', 'G-LMZ4WN7LCM');
</script>
    <script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3332997411212854"
     crossorigin="anonymous"></script>
</head>

<body data-page="detail">
    <!--====== HEADER ONE PART START ======-->
    <header class="header-area">
        <div class="navbar-area navbar-one navbar-transparent">
    <div class="container">
        <div class="row">
            <div class="col-lg-12">
                <nav class="navbar navbar-expand-lg">
                    <a class="navbar-brand" href="/">
                                        <span>
                        V2ray免费机场                    </span>
                                        </a>
                    <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarOne" aria-controls="navbarOne" aria-expanded="false" aria-label="Toggle navigation">
                        <span class="toggler-icon"></span>
                        <span class="toggler-icon"></span>
                        <span class="toggler-icon"></span>
                    </button>
                    <div class="collapse navbar-collapse sub-menu-bar" id="navbarOne">
                        <ul class="navbar-nav m-auto">
                                                        <li class="nav-item">
                                <a href="/">首页</a>
                            </li>
                                                        <li class="nav-item">
                                <a href="/free-nodes/">免费节点</a>
                            </li>
                                                        <li class="nav-item">
                                <a href="/paid-subscribe/">推荐机场</a>
                            </li>
                                                        <li class="nav-item">
                                <a href="/news/">新闻资讯</a>
                            </li>
                                                        <li class="nav-item">
                                <a href="/client.htm">客户端</a>
                            </li>
                                                    </ul>
                    </div>
                </nav> <!-- navbar -->
            </div>
        </div> <!-- row -->
    </div> <!-- container -->
</div>
        <div id="home" class="header-content-area d-flex align-items-center list" style="height:450px;">
            <div class="container">
                <div class="row">
                    <div class="col-lg-12">
                        <div class="header-wrapper">
                            <div class="header-content">
                                <h1 class="header-title">JavaWeb项目基于SpringBoot框架实现统一的数据返回格式 + 统一异常处理 + 统一日志处理</h1>
                                <p class="text nav">
                                    <a href="/">首页</a> / <a href="/news/">新闻资讯</a> / <span>正文</span>
                                </p>
                            </div> <!-- header content -->
                            <div class="header-image d-none d-lg-block">
                                <div class="image">
                                    <img src="/assets/website/img/surfboardnode/header.png" alt="Header">
                                </div>
                            </div>
                        </div>
                    </div>
                </div> <!-- row -->
            </div> <!-- container -->
            <div class="header-shape">
                <img src="/assets/website/img/surfboardnode/header-shape.svg" alt="shape">
            </div> <!-- header-shape -->
        </div> <!-- header content area -->
    </header>
    <!--====== HEADER ONE PART ENDS ======-->
    <!--====== ABOUT THREE PART START ======-->
    <section id="about" class="about-area pt-70 pb-100">
        <div class="container">
            <div class="row">
                <div class="col-md-9">
                                    <input type="hidden" id="share-website-info" data-name="Clash Node官网订阅站" data-url="https://clashnode.github.io">
                <div class="xcblog-blog-detail">
                      				  				  				<div id="content_views" class="htmledit_views"> <p><img decoding="async" alt="" src="http://img.555519.xyz/uploads3/20220609/3526a3ffede50633c0b2883a85534ba3.jpg"></p> <p id="main-toc"><strong>目录</strong></p> <p id="%F0%9F%94%94%E5%89%8D%E8%A8%80-toc" style="margin-left:0px;">????前言</p> <p id="%F0%9F%94%94%E7%BB%9F%E4%B8%80%E8%BF%94%E5%9B%9E%E6%A0%BC%E5%BC%8F-toc" style="margin-left:0px;">????统一返回格式</p> <p id="%C2%A0%E6%B7%BB%E5%8A%A0%E6%9E%9A%E4%B8%BE%E7%B1%BB-toc" style="margin-left:40px;">&nbsp;添加枚举类</p> <p id="%C2%A0%E6%B7%BB%E5%8A%A0%E7%BB%9F%E4%B8%80%E8%BF%94%E5%9B%9E%E6%A0%BC%E5%BC%8F%E7%9A%84%E7%B1%BB-toc" style="margin-left:40px;">&nbsp;添加统一返回格式的类</p> <p id="%C2%A0%E6%B5%8B%E8%AF%95-toc" style="margin-left:0px;">&nbsp;测试</p> <p id="%F0%9F%94%94%E7%BB%9F%E4%B8%80%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86-toc" style="margin-left:0px;">????统一异常处理</p> <p id="%C2%A0%E6%B7%BB%E5%8A%A0%E7%BB%9F%E4%B8%80%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86%E5%99%A8%EF%BC%88%E5%88%9B%E5%BB%BA%E4%B8%80%E4%B8%AAhandler%E5%8C%85%EF%BC%8C%E5%9C%A8%E8%AF%A5%E5%8C%85%E4%B8%8B%E9%9D%A2%E6%B7%BB%E5%8A%A0GlobalExceptionHandler%E7%B1%BB%EF%BC%89-toc" style="margin-left:40px;">&nbsp;添加统一异常处理器（创建一个handler包，在该包下面添加GlobalExceptionHandler类）</p> <p id="%E6%B5%8B%E8%AF%95%E7%BB%9F%E4%B8%80%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86-toc" style="margin-left:40px;">测试统一异常处理</p> <p id="%E6%B7%BB%E5%8A%A0%E8%87%AA%E5%AE%9A%E4%B9%89%E5%BC%82%E5%B8%B8%E7%B1%BB%20%EF%BC%88%E6%96%B0%E5%BB%BAexception%E5%8C%85%EF%BC%8C%E5%9C%A8%E8%AF%A5%E5%8C%85%E4%B8%8B%E6%B7%BB%E5%8A%A0%E8%87%AA%E5%AE%9A%E4%B9%89%E5%BC%82%E5%B8%B8%E7%B1%BB%EF%BC%89-toc" style="margin-left:40px;">添加自定义异常类 （新建exception包，在该包下添加自定义异常类）</p> <p id="%C2%A0%E5%9C%A8%E7%BB%9F%E4%B8%80%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86%E7%B1%BBGlobalExceptionHandler%E4%B8%AD%E6%B7%BB%E5%8A%A0%E4%B8%80%E4%B8%AA%E8%87%AA%E5%AE%9A%E4%B9%89%E5%BC%82%E5%B8%B8%E7%9A%84%E5%A4%84%E7%90%86-toc" style="margin-left:40px;">&nbsp;在统一异常处理类GlobalExceptionHandler中添加一个自定义异常的处理</p> <p id="%C2%A0%E6%B5%8B%E8%AF%95%E8%87%AA%E5%AE%9A%E4%B9%89%E5%BC%82%E5%B8%B8-toc" style="margin-left:40px;">&nbsp;测试自定义异常</p> <p id="%F0%9F%94%94%E7%BB%9F%E4%B8%80%E6%97%A5%E5%BF%97%E5%A4%84%E7%90%86-toc" style="margin-left:0px;">????统一日志处理</p> <p id="%E6%B7%BB%E5%8A%A0%E6%97%A5%E5%BF%97%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6-toc" style="margin-left:40px;">添加日志配置文件</p> <p id="%E6%B7%BB%E5%8A%A0application.properties%E9%85%8D%E7%BD%AE-toc" style="margin-left:40px;">添加application.properties配置</p> <p id="%C2%A0%E4%BF%AE%E6%94%B9GlobalExceptionHandler%E7%B1%BB-toc" style="margin-left:40px;">&nbsp;修改GlobalExceptionHandler类</p> <p id="%E6%B5%8B%E8%AF%95%E6%95%88%E6%9E%9C%C2%A0-toc" style="margin-left:40px;">测试效果</p> <p id="%E5%AE%9A%E4%B9%89%E5%B7%A5%E5%85%B7%E7%B1%BB%EF%BC%88%E6%96%B0%E5%BB%BAutils%E5%8C%85%EF%BC%8C%E5%9C%A8%E8%AF%A5%E5%8C%85%E4%B8%8B%E6%B7%BB%E5%8A%A0ExceptionUtils%E7%B1%BB%EF%BC%89%C2%A0-toc" style="margin-left:40px;">定义工具类（新建utils包，在该包下添加ExceptionUtils类）</p> <p id="%E5%86%8D%E4%BF%AE%E6%94%B9GlobalExceptionHandler%E7%B1%BB-toc" style="margin-left:40px;">再修改GlobalExceptionHandler类</p> <p id="%E6%B5%8B%E8%AF%95%E6%95%88%E6%9E%9C%C2%A0-toc" style="margin-left:40px;">测试效果</p> <p id="%F0%9F%94%94%E6%80%BB%E7%BB%93%EF%BC%88%E6%BA%90%E4%BB%A3%E7%A0%81%EF%BC%89-toc" style="margin-left:0px;">????总结（源代码）</p> <hr id="hr-toc"/> </h1> <p>大家好啊，我是爷爷的茶七里香，在我们的项目开发中，我们都会对数据返回格式进行统一的处理，这样可以方便前端人员取数据，当然除了正常流程的数据返回格式需要统一以外，我们也需要对异常的情况进行统一的处理，以及项目必备的日志这些，以下都会说明如何添加，写该文的目的是为了让新人了解在项目开发中，这些是咋做的。</p> </h1> <blockquote> <p>在项目开发中返回的是json格式的数据，也就是统一json数据返回格式，一般情况下返回数据的基本格式包含是否成功、响应状态码、返回的消息、以及返回的数据。格式如下：</p> </blockquote> <pre><code class="language-java">{   "success": 布尔, 	   // 是否成功   "code": 数字, 	   // 响应状态码   "message": 字符串,   // 返回的消息   "data": {}          //  放置响应的数据 }</code></pre> <p>下面开始实操：</p> <ul> <li> <h2 id="%C2%A0%E6%B7%BB%E5%8A%A0%E6%9E%9A%E4%B8%BE%E7%B1%BB">&nbsp;添加枚举类</h2> </li> </ul> <blockquote> <p>该类定义了以上统一格式的前三部分：是否成功、响应状态码、返回的消息；可自行根据项目需要进行后续的添加或者删改。</p> </blockquote> <p>创建一个result包，下面放置ResultCodeEnum枚举类</p> <pre><code class="language-java">/**  * 状态码  *  * @author 爷爷的茶七里香  * @date 2022/05/30  */ public enum ResultCodeEnum {      SUCCESS(true, 20000, "成功"),      UNKNOWN_REASON(false, 20001, "未知错误");      private final Boolean success;      private final Integer code;      private final String message;      ResultCodeEnum(Boolean success, Integer code, String message) {         this.success = success;         this.code = code;         this.message = message;     }      public Boolean getSuccess() {         return success;     }      public Integer getCode() {         return code;     }      public String getMessage() {         return message;     }      @Override     public String toString() {         return "ResultCodeEnum{" + "success=" + success + ", code=" + code + ", message='" + message + '\'' + '}';     } }</code></pre> <ul> <li> <h2 id="%C2%A0%E6%B7%BB%E5%8A%A0%E7%BB%9F%E4%B8%80%E8%BF%94%E5%9B%9E%E6%A0%BC%E5%BC%8F%E7%9A%84%E7%B1%BB">&nbsp;添加统一返回格式的类</h2> </li> </ul> <blockquote> <p>该类是用来和前端交互的类，定义的就是本文开头所说的格式</p> </blockquote> <p>在result包下创建一个统一返回格式的类R</p> <pre><code class="language-java">/**  * 统一返回格式类  *  * @author 爷爷的茶七里香  * @date 2022/05/30  */ public class R {      /**      * 是否成功      */     private Boolean success;      /**      * 状态码      */     private Integer code;      /**      * 返回的消息      */     private String message;      /**      * 放置响应的数据      */     private Map&lt;String, Object&gt; data = new HashMap&lt;&gt;();      public R() {}      /** 以下是定义一些常用到的格式，可以看到调用了我们创建的枚举类 */          public static R ok() {         R r = new R();         r.setSuccess(ResultCodeEnum.SUCCESS.getSuccess());         r.setCode(ResultCodeEnum.SUCCESS.getCode());         r.setMessage(ResultCodeEnum.SUCCESS.getMessage());         return r;     }      public static R error() {         R r = new R();         r.setSuccess(ResultCodeEnum.UNKNOWN_REASON.getSuccess());         r.setCode(ResultCodeEnum.UNKNOWN_REASON.getCode());         r.setMessage(ResultCodeEnum.UNKNOWN_REASON.getMessage());         return r;     }      public static R setResult(ResultCodeEnum resultCodeEnum) {         R r = new R();         r.setSuccess(resultCodeEnum.getSuccess());         r.setCode(resultCodeEnum.getCode());         r.setMessage(resultCodeEnum.getMessage());         return r;     }      public R success(Boolean success) {         this.setSuccess(success);         return this;     }      public R message(String message) {         this.setMessage(message);         return this;     }      public R code(Integer code) {         this.setCode(code);         return this;     }      public R data(String key, Object value) {         this.data.put(key, value);         return this;     }      public R data(Map&lt;String, Object&gt; map) {         this.setData(map);         return this;     }      /** 以下是get/set方法，如果项目有集成lombok可以使用@Data注解代替 */      public Boolean getSuccess() {         return success;     }      public void setSuccess(Boolean success) {         this.success = success;     }      public Integer getCode() {         return code;     }      public void setCode(Integer code) {         this.code = code;     }      public String getMessage() {         return message;     }      public void setMessage(String message) {         this.message = message;     }      public Map&lt;String, Object&gt; getData() {         return data;     }      public void setData(Map&lt;String, Object&gt; data) {         this.data = data;     } }</code></pre> <ul> <li> </h1> </li> </ul> <p>下面我们提供一个controller来测试下</p> <pre><code class="language-java">/**  * 测试控制器  *  * @author 爷爷的茶七里香  * @date 2022/05/30  */ @RestController @RequestMapping("testR") public class TestController {      /**      * @return {@link R}      */     @GetMapping("ok")     public R testOk() {         Map&lt;String, Object&gt; data = new HashMap&lt;&gt;();         data.put("name", "李太白");         return R.ok().data(data);     } }</code></pre> <p>效果：</p> <p><img fetchpriority="high" decoding="async" alt="" height="548" src="http://img.555519.xyz/uploads3/20220609/dabc8da7b58f787d0e34bc1b5e57a87b.jpg"></p> <blockquote> <p>可以看到格式是正确的，只要我们返回数据的时候使用R这个类返回就行了，不过有一种情况，就是当我们代码中抛出异常之后返回的格式就不是这样子了，下面我演示一下在代码中添加int a = 1/0的语句，肯定导致抛异常的；</p> </blockquote> <p><img decoding="async" alt="" height="315" src="http://img.555519.xyz/uploads3/20220609/c9ee8e6fb24802faa769519468e19182.jpg"></p> <p>&nbsp;结果：</p> <p><img decoding="async" alt="" height="522" src="http://img.555519.xyz/uploads3/20220609/f0799e9177d8a657134c830d79a31454.jpg"></p> <blockquote> <p>&nbsp;可以发现返回的格式已经不是我们所需要的格式了，这种情况会给前端人员带来不必要的麻烦，所以我们也需要对异常情况进行统一的格式处理；</p> </blockquote> </h1> <blockquote> <p>&nbsp;经过上面的演示，相信你已经明白我们为什么需要进行统一的异常处理了，当然处理统一的异常处理以外我们在开发项目中也会主动的抛出异常，像这种情况我们需要配合自定义异常来完成；</p> </blockquote> <p>下面进行实操：</p> <ul> <li> <h2 id="%C2%A0%E6%B7%BB%E5%8A%A0%E7%BB%9F%E4%B8%80%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86%E5%99%A8%EF%BC%88%E5%88%9B%E5%BB%BA%E4%B8%80%E4%B8%AAhandler%E5%8C%85%EF%BC%8C%E5%9C%A8%E8%AF%A5%E5%8C%85%E4%B8%8B%E9%9D%A2%E6%B7%BB%E5%8A%A0GlobalExceptionHandler%E7%B1%BB%EF%BC%89">&nbsp;添加统一异常处理器（创建一个handler包，在该包下面添加GlobalExceptionHandler类）</h2> </li> </ul> <pre><code class="language-java">/**  * 统一异常处理  *  * @author 爷爷的茶七里香  * @date 2022/05/30 ControllerAdvice注解的含义是当异常抛到controller层时会拦截下来  */ @ControllerAdvice public class GlobalExceptionHandler {      /**      * 使用ExceptionHandler注解声明处理Exception异常      *      * @param e e      * @return {@link R}      */     @ResponseBody     @ExceptionHandler(Exception.class)     public R exception(Exception e) {         // 控制台打印异常         e.printStackTrace();         // 返回错误格式信息         return R.error();     }  }</code></pre> <ul> <li> <h2 id="%E6%B5%8B%E8%AF%95%E7%BB%9F%E4%B8%80%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86">测试统一异常处理</h2> </li> </ul> <p><img loading="lazy" decoding="async" alt="" height="492" src="http://img.555519.xyz/uploads3/20220609/d6e8927ca0d29a9d16b2f225a2646554.jpg"></p> <blockquote> <p>可以看到现在出现异常之后返回的格式已经是我们所需要的格式了，如果我们想让这个错误信息更加明确，我们可以通过添加自定义异常来实现，下面开始实操：</p> </blockquote> <ul> <li> <h2 id="%E6%B7%BB%E5%8A%A0%E8%87%AA%E5%AE%9A%E4%B9%89%E5%BC%82%E5%B8%B8%E7%B1%BB%20%EF%BC%88%E6%96%B0%E5%BB%BAexception%E5%8C%85%EF%BC%8C%E5%9C%A8%E8%AF%A5%E5%8C%85%E4%B8%8B%E6%B7%BB%E5%8A%A0%E8%87%AA%E5%AE%9A%E4%B9%89%E5%BC%82%E5%B8%B8%E7%B1%BB%EF%BC%89">添加自定义异常类 （新建exception包，在该包下添加自定义异常类）</h2> </li> </ul> <pre><code class="language-java">/**  * 测试自定义异常类  *  * @author 爷爷的茶七里香  * @date 2022/05/30 需要继承运行时异常RuntimeException  */ public class TestException extends RuntimeException {     private Integer code;      public TestException(ResultCodeEnum resultCodeEnum) {         // 调用父类的方法添加信息         super(resultCodeEnum.getMessage());         this.code = resultCodeEnum.getCode();     }      public Integer getCode() {         return code;     } }</code></pre> <ul> <li> <h2 id="%C2%A0%E5%9C%A8%E7%BB%9F%E4%B8%80%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86%E7%B1%BBGlobalExceptionHandler%E4%B8%AD%E6%B7%BB%E5%8A%A0%E4%B8%80%E4%B8%AA%E8%87%AA%E5%AE%9A%E4%B9%89%E5%BC%82%E5%B8%B8%E7%9A%84%E5%A4%84%E7%90%86">&nbsp;在统一异常处理类GlobalExceptionHandler中添加一个自定义异常的处理</h2> </li> </ul> <pre><code class="language-java">/**  * 统一异常处理  *  * @author 爷爷的茶七里香  * @date 2022/05/30 ControllerAdvice注解的含义是当异常抛到controller层时会拦截下来  */ @ControllerAdvice public class GlobalExceptionHandler {      /**      * 使用ExceptionHandler注解声明处理Exception异常      *      * @param e e      * @return {@link R}      */     @ResponseBody     @ExceptionHandler(Exception.class)     public R exception(Exception e) {         // 控制台打印异常         e.printStackTrace();         // 返回错误格式信息         return R.error();     }      /**      * 使用ExceptionHandler注解声明处理TestException异常      *      * @param e e      * @return {@link R}      */     @ResponseBody     @ExceptionHandler(TestException.class)     public R exception(TestException e) {         // 控制台打印异常         e.printStackTrace();         // 返回错误格式信息         return R.error().message(e.getMessage()).code(e.getCode());     }  }</code></pre> <ul> <li> <h2 id="%C2%A0%E6%B5%8B%E8%AF%95%E8%87%AA%E5%AE%9A%E4%B9%89%E5%BC%82%E5%B8%B8">&nbsp;测试自定义异常</h2> </li> </ul> <p>在枚举类中添加一个状态信息</p> <pre><code class="language-java">TEST_NUMBER(false, 500, "计算错误");</code></pre> <p><img loading="lazy" decoding="async" alt="" height="449" src="http://img.555519.xyz/uploads3/20220609/bb53172c65f661505773fc5539ed727a.jpg"></p> <p>&nbsp;输出结果：</p> <p><img loading="lazy" decoding="async" alt="" height="499" src="http://img.555519.xyz/uploads3/20220609/5d048fdc513eb123b29f900cc1252075.jpg"></p> <blockquote> <p>现在自定义异常也能统一返回格式了</p> </blockquote> </h1> <blockquote> <p>为了更方便我们进行错误的调式，一般会在项目中集成日志，下面我们开始实操吧：</p> </blockquote> <ul> <li> <h2 id="%E6%B7%BB%E5%8A%A0%E6%97%A5%E5%BF%97%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6">添加日志配置文件</h2> </li> </ul> <blockquote> <p>在resources下添加日志的配置，文件名必须是logback-spring.xml</p> <p>以下配置一般不需要修改，要改的话也只是修改日志的输出目录</p> <p>&lt;property name="log.path" value="D:/javaWeb/log" /&gt;</p> <p>value就是日志的输出位置</p> </blockquote> <pre><code class="language-XML">&lt;?xml version="1.0" encoding="UTF-8"?&gt; &lt;configuration  scan="true" scanPeriod="10 seconds"&gt;     &lt;!-- 日志级别从低到高分为TRACE &lt; DEBUG &lt; INFO &lt; WARN &lt; ERROR &lt; FATAL，如果设置为WARN，则低于WARN的信息都不会输出 --&gt;     &lt;!-- scan:当此属性设置为true时，配置文件如果发生改变，将会被重新加载，默认值为true --&gt;     &lt;!-- scanPeriod:设置监测配置文件是否有修改的时间间隔，如果没有给出时间单位，默认单位是毫秒。当scan为true时，此属性生效。默认的时间间隔为1分钟。 --&gt;     &lt;!-- debug:当此属性设置为true时，将打印出logback内部日志信息，实时查看logback运行状态。默认值为false。 --&gt;     &lt;contextName&gt;logback&lt;/contextName&gt;     &lt;!-- name的值是变量的名称，value的值时变量定义的值。通过定义的值会被插入到logger上下文中。定义变量后，可以使“${}”来使用变量。 --&gt;     &lt;property name="log.path" value="D:/javaWeb/log" /&gt;      &lt;!--控制台日志格式：彩色日志--&gt;     &lt;!-- magenta:洋红 --&gt;     &lt;!-- boldMagenta:粗红--&gt;     &lt;!-- cyan:青色 --&gt;     &lt;!-- white:白色 --&gt;     &lt;!-- magenta:洋红 --&gt;     &lt;property name="CONSOLE_LOG_PATTERN"               value="%yellow(%date{yyyy-MM-dd HH:mm:ss}) |%highlight(%-5level) |%blue(%thread) |%blue(%file:%line) |%green(%logger) |%cyan(%msg%n)"/&gt;      &lt;!--文件日志格式--&gt;     &lt;property name="FILE_LOG_PATTERN"               value="%date{yyyy-MM-dd HH:mm:ss} |%-5level |%thread |%file:%line |%logger |%msg%n" /&gt;      &lt;!--编码--&gt;     &lt;property name="ENCODING"               value="UTF-8" /&gt;      &lt;!--输出到控制台--&gt;     &lt;appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"&gt;         &lt;filter class="ch.qos.logback.classic.filter.ThresholdFilter"&gt;             &lt;!--日志级别--&gt;             &lt;level&gt;DEBUG&lt;/level&gt;         &lt;/filter&gt;         &lt;encoder&gt;             &lt;!--日志格式--&gt;             &lt;Pattern&gt;${CONSOLE_LOG_PATTERN}&lt;/Pattern&gt;             &lt;!--日志字符集--&gt;             &lt;charset&gt;${ENCODING}&lt;/charset&gt;         &lt;/encoder&gt;     &lt;/appender&gt;      &lt;!--输出到文件--&gt;     &lt;appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"&gt;         &lt;!--日志过滤器：此日志文件只记录INFO级别的--&gt;         &lt;filter class="ch.qos.logback.classic.filter.LevelFilter"&gt;             &lt;level&gt;INFO&lt;/level&gt;             &lt;onMatch&gt;ACCEPT&lt;/onMatch&gt;             &lt;onMismatch&gt;DENY&lt;/onMismatch&gt;         &lt;/filter&gt;         &lt;!-- 正在记录的日志文件的路径及文件名 --&gt;         &lt;file&gt;${log.path}/log_info.log&lt;/file&gt;         &lt;encoder&gt;             &lt;pattern&gt;${FILE_LOG_PATTERN}&lt;/pattern&gt;             &lt;charset&gt;${ENCODING}&lt;/charset&gt;         &lt;/encoder&gt;         &lt;!-- 日志记录器的滚动策略，按日期，按大小记录 --&gt;         &lt;rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"&gt;             &lt;!-- 每天日志归档路径以及格式 --&gt;             &lt;fileNamePattern&gt;${log.path}/info/log-info-%d{yyyy-MM-dd}.%i.log&lt;/fileNamePattern&gt;             &lt;timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"&gt;                 &lt;maxFileSize&gt;500MB&lt;/maxFileSize&gt;             &lt;/timeBasedFileNamingAndTriggeringPolicy&gt;             &lt;!--日志文件保留天数--&gt;             &lt;maxHistory&gt;15&lt;/maxHistory&gt;         &lt;/rollingPolicy&gt;     &lt;/appender&gt;      &lt;appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"&gt;         &lt;!-- 日志过滤器：此日志文件只记录WARN级别的 --&gt;         &lt;filter class="ch.qos.logback.classic.filter.LevelFilter"&gt;             &lt;level&gt;WARN&lt;/level&gt;             &lt;onMatch&gt;ACCEPT&lt;/onMatch&gt;             &lt;onMismatch&gt;DENY&lt;/onMismatch&gt;         &lt;/filter&gt;         &lt;!-- 正在记录的日志文件的路径及文件名 --&gt;         &lt;file&gt;${log.path}/log_warn.log&lt;/file&gt;         &lt;encoder&gt;             &lt;pattern&gt;${FILE_LOG_PATTERN}&lt;/pattern&gt;             &lt;charset&gt;${ENCODING}&lt;/charset&gt; &lt;!-- 此处设置字符集 --&gt;         &lt;/encoder&gt;         &lt;!-- 日志记录器的滚动策略，按日期，按大小记录 --&gt;         &lt;rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"&gt;             &lt;fileNamePattern&gt;${log.path}/warn/log-warn-%d{yyyy-MM-dd}.%i.log&lt;/fileNamePattern&gt;             &lt;timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"&gt;                 &lt;maxFileSize&gt;100MB&lt;/maxFileSize&gt;             &lt;/timeBasedFileNamingAndTriggeringPolicy&gt;             &lt;!--日志文件保留天数--&gt;             &lt;maxHistory&gt;15&lt;/maxHistory&gt;         &lt;/rollingPolicy&gt;     &lt;/appender&gt;      &lt;appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"&gt;         &lt;!-- 日志过滤器：此日志文件只记录ERROR级别的 --&gt;         &lt;filter class="ch.qos.logback.classic.filter.LevelFilter"&gt;             &lt;level&gt;ERROR&lt;/level&gt;             &lt;onMatch&gt;ACCEPT&lt;/onMatch&gt;             &lt;onMismatch&gt;DENY&lt;/onMismatch&gt;         &lt;/filter&gt;         &lt;!-- 正在记录的日志文件的路径及文件名 --&gt;         &lt;file&gt;${log.path}/log_error.log&lt;/file&gt;         &lt;encoder&gt;             &lt;pattern&gt;${FILE_LOG_PATTERN}&lt;/pattern&gt;             &lt;charset&gt;${ENCODING}&lt;/charset&gt; &lt;!-- 此处设置字符集 --&gt;         &lt;/encoder&gt;         &lt;!-- 日志记录器的滚动策略，按日期，按大小记录 --&gt;         &lt;rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"&gt;             &lt;fileNamePattern&gt;${log.path}/error/log-error-%d{yyyy-MM-dd}.%i.log&lt;/fileNamePattern&gt;             &lt;timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"&gt;                 &lt;maxFileSize&gt;100MB&lt;/maxFileSize&gt;             &lt;/timeBasedFileNamingAndTriggeringPolicy&gt;             &lt;!--日志文件保留天数--&gt;             &lt;maxHistory&gt;15&lt;/maxHistory&gt;         &lt;/rollingPolicy&gt;     &lt;/appender&gt;      &lt;!--开发环境--&gt;     &lt;springProfile name="dev"&gt;         &lt;!--可以灵活设置此处，从而控制日志的输出--&gt;         &lt;root level="INFO"&gt;             &lt;appender-ref ref="CONSOLE" /&gt;             &lt;appender-ref ref="INFO_FILE" /&gt;             &lt;appender-ref ref="WARN_FILE" /&gt;             &lt;appender-ref ref="ERROR_FILE" /&gt;         &lt;/root&gt;     &lt;/springProfile&gt;      &lt;!--生产环境--&gt;     &lt;springProfile name="pro"&gt;         &lt;root level="ERROR"&gt;             &lt;appender-ref ref="ERROR_FILE" /&gt;         &lt;/root&gt;     &lt;/springProfile&gt;  &lt;/configuration&gt;</code></pre> <ul> <li> <h2 id="%E6%B7%BB%E5%8A%A0application.properties%E9%85%8D%E7%BD%AE">添加application.properties配置</h2> </li> </ul> <blockquote> <p>&nbsp;配置文件需要设置下环境，需要跟日志配置文件中的&lt;springProfile name="dev"&gt;对应上，不然不生效</p> </blockquote> <pre><code class="language-XML"># 设置环境 spring.profiles.active=dev</code></pre> <ul> <li> <h2 id="%C2%A0%E4%BF%AE%E6%94%B9GlobalExceptionHandler%E7%B1%BB">&nbsp;修改GlobalExceptionHandler类</h2> </li> </ul> <blockquote> <p>具体修改看下面的代码：</p> </blockquote> <pre><code class="language-java">/**  * 统一异常处理  *  * @author 爷爷的茶七里香  * @date 2022/05/30 ControllerAdvice注解的含义是当异常抛到controller层时会拦截下来  */ @ControllerAdvice public class GlobalExceptionHandler {      /**      * 打印日志       * 如果项目有集成lombok可使用@Slf4j注解代替      */     private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);      /**      * 使用ExceptionHandler注解声明处理Exception异常      *      * @param e e      * @return {@link R}      */     @ResponseBody     @ExceptionHandler(Exception.class)     public R exception(Exception e) {         // 控制台打印异常         log.error(e.getMessage());         // 返回错误格式信息         return R.error();     }      /**      * 使用ExceptionHandler注解声明处理TestException异常      *      * @param e e      * @return {@link R}      */     @ResponseBody     @ExceptionHandler(TestException.class)     public R exception(TestException e) {         // 控制台打印异常         log.error(e.getMessage());         // 返回错误格式信息         return R.error().message(e.getMessage()).code(e.getCode());     }  }</code></pre> <ul> <li> <h2 id="%E6%B5%8B%E8%AF%95%E6%95%88%E6%9E%9C%C2%A0">测试效果</h2> </li> </ul> <p><img loading="lazy" decoding="async" alt="" height="1042" src="http://img.555519.xyz/uploads3/20220609/fcc9567a21f8e3be31d975d1dfcf5667.jpg"></p> <blockquote> <p>&nbsp;日志生效了，而且在我们的D盘javaWeb目录下也有对应的日志文件了</p> </blockquote> <p><img loading="lazy" decoding="async" alt="" height="208" src="http://img.555519.xyz/uploads3/20220609/4007283f5cfe40493ba9916f1ebd5b88.jpg"></p> <blockquote> <p>&nbsp;我们可以进一步的完善下，将日志堆栈信息输出到文件</p> </blockquote> <ul> <li> <h2 id="%E5%AE%9A%E4%B9%89%E5%B7%A5%E5%85%B7%E7%B1%BB%EF%BC%88%E6%96%B0%E5%BB%BAutils%E5%8C%85%EF%BC%8C%E5%9C%A8%E8%AF%A5%E5%8C%85%E4%B8%8B%E6%B7%BB%E5%8A%A0ExceptionUtils%E7%B1%BB%EF%BC%89%C2%A0">定义工具类（新建utils包，在该包下添加ExceptionUtils类）</h2> </li> </ul> <pre><code class="language-java">/**  * 日志堆栈信息输出到文件工具类  *  * @author 爷爷的茶七里香  * @date 2022/05/30  */ public class ExceptionUtils {     public static String getMessage(Exception e) {         StringWriter sw = null;         PrintWriter pw = null;         try {             sw = new StringWriter();             pw = new PrintWriter(sw);             // 将出错的栈信息输出到printWriter中             e.printStackTrace(pw);             pw.flush();             sw.flush();         } finally {             if (sw != null) {                 try {                     sw.close();                 } catch (IOException e1) {                     e1.printStackTrace();                 }             }             if (pw != null) {                 pw.close();             }         }         return sw.toString();     } }</code></pre> <ul> <li> <h2 id="%E5%86%8D%E4%BF%AE%E6%94%B9GlobalExceptionHandler%E7%B1%BB">再修改GlobalExceptionHandler类</h2> </li> </ul> <pre><code class="language-java">/**  * 统一异常处理  *  * @author 爷爷的茶七里香  * @date 2022/05/30 ControllerAdvice注解的含义是当异常抛到controller层时会拦截下来  */ @ControllerAdvice public class GlobalExceptionHandler {      /**      * 打印日志 如果项目有集成lombok可使用@Slf4j注解代替      */     private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);      /**      * 使用ExceptionHandler注解声明处理Exception异常      *      * @param e e      * @return {@link R}      */     @ResponseBody     @ExceptionHandler(Exception.class)     public R exception(Exception e) {         // 控制台打印异常  借助工具类将错误堆栈输出到文件         log.error(ExceptionUtils.getMessage(e));         // 返回错误格式信息         return R.error();     }      /**      * 使用ExceptionHandler注解声明处理TestException异常      *      * @param e e      * @return {@link R}      */     @ResponseBody     @ExceptionHandler(TestException.class)     public R exception(TestException e) {         // 控制台打印异常   借助工具类将错误堆栈输出到文件         log.error(ExceptionUtils.getMessage(e));         // 返回错误格式信息         return R.error().message(e.getMessage()).code(e.getCode());     }  }</code></pre> <ul> <li> <h2>测试效果</h2> </li> </ul> <p><img loading="lazy" decoding="async" alt="" height="1042" src="http://img.555519.xyz/uploads3/20220609/7b983d4a880e1a23e13ab44ca9cdff25.jpg"></p> </h1> <blockquote> <p>&nbsp;到这就已经完成了统一返回格式、统一异常处理、已经统一的日志处理，通过文章不一定能看明白，所以我在此提供源代码，配合文章食用;</p> </blockquote> <p><span class="link-card-box"><span class="link-title">gitee仓库（本文章源代码）</span><span class="link-link"><img decoding="async" alt="" class="link-link-icon" src="http://img.555519.xyz/uploads3/20220609/bf924185496dfe1f525d6fb6826d92ff.jpg">https://gitee.com/sgdygb/demo20220530</span></span></p> </div> 			                </div>
                <div class="clearfix"></div>
                <div class="col-md-12 mt-5">
                                        <p>上一个：<a href="/news/article-99544.htm">动物医院资质要求标准（动物医院资质要求标准是多少）</a></p>
                                        <p>下一个：<a href="/news/article-100332.htm">同城免费领养猫猫（附近领养猫的地方）</a></p>
                                    </div>
                                </div>
                <div class="col-md-3">
                    <div class="panel panel-default">
    <div class="panel-heading">
        <h3 class="panel-title">热门文章</h3>
    </div>
    <div class="panel-body">
        <ul class="p-0 x-0" style="list-style: none;margin: 0;padding: 0;">
                        <li class="py-2"><a href="/news/article-89855.htm" title="我们如何使用MySQL SELECT语句计算表中的行数？">我们如何使用MySQL SELECT语句计算表中的行数？</a></li>
                        <li class="py-2"><a href="/free-nodes/2025-4-9-free-v2ray.htm" title="V2ray免费VPN机场 | 4月9日20.8M/S|免费Shadowrocket/Clash/SSR/V2ray订阅节点地址分享">V2ray免费VPN机场 | 4月9日20.8M/S|免费Shadowrocket/Clash/SSR/V2ray订阅节点地址分享</a></li>
                        <li class="py-2"><a href="/news/article-83404.htm" title="Spring Security核心类详解">Spring Security核心类详解</a></li>
                        <li class="py-2"><a href="/free-nodes/2025-4-22-node-share.htm" title="V2ray免费VPN机场 | 4月22日21.6M/S|免费Shadowrocket/SSR/Clash/V2ray订阅节点地址分享">V2ray免费VPN机场 | 4月22日21.6M/S|免费Shadowrocket/SSR/Clash/V2ray订阅节点地址分享</a></li>
                        <li class="py-2"><a href="/free-nodes/2025-4-23-node-share-links.htm" title="V2ray免费VPN机场 | 4月23日22.1M/S|免费Shadowrocket/SSR/Clash/V2ray订阅节点地址分享">V2ray免费VPN机场 | 4月23日22.1M/S|免费Shadowrocket/SSR/Clash/V2ray订阅节点地址分享</a></li>
                        <li class="py-2"><a href="/free-nodes/2025-4-19-free-node-subscribe-links.htm" title="V2ray免费VPN机场 | 4月19日21M/S|免费Shadowrocket/Clash/V2ray/SSR订阅节点地址分享">V2ray免费VPN机场 | 4月19日21M/S|免费Shadowrocket/Clash/V2ray/SSR订阅节点地址分享</a></li>
                        <li class="py-2"><a href="/news/article-92841.htm" title="开个宠物诊所需要什么资质（想开宠物诊所需要什么）">开个宠物诊所需要什么资质（想开宠物诊所需要什么）</a></li>
                        <li class="py-2"><a href="/news/article-89116.htm" title="动物疫苗价格表图片高清大全集视频（动物疫苗接种时间表）">动物疫苗价格表图片高清大全集视频（动物疫苗接种时间表）</a></li>
                        <li class="py-2"><a href="/free-nodes/2025-4-15-node-share.htm" title="V2ray免费VPN机场 | 4月15日22.1M/S|免费V2ray/SSR/Shadowrocket/Clash订阅节点地址分享">V2ray免费VPN机场 | 4月15日22.1M/S|免费V2ray/SSR/Shadowrocket/Clash订阅节点地址分享</a></li>
                        <li class="py-2"><a href="/free-nodes/2025-4-18-free-v2ray-subscribe.htm" title="V2ray免费VPN机场 | 4月18日20.8M/S|免费Clash/SSR/Shadowrocket/V2ray订阅节点地址分享">V2ray免费VPN机场 | 4月18日20.8M/S|免费Clash/SSR/Shadowrocket/V2ray订阅节点地址分享</a></li>
                    </ul>
    </div>
</div>

<div class="panel panel-default">
    <div class="panel-heading">
        <h3 class="panel-title">归纳</h3>
    </div>
    <div class="panel-body">
        <ul class="p-0 x-0" style="list-style: none;margin: 0;padding: 0;">
                        <li class="py-2">
                <h4><span class="badge" style="float: right;">68</span> <a href="/date/2025-04/" title="2025-04 归档">2025-04</a></h4>
            </li>
                        <li class="py-2">
                <h4><span class="badge" style="float: right;">15</span> <a href="/date/2025-03/" title="2025-03 归档">2025-03</a></h4>
            </li>
                    </ul>
    </div>
</div>

                </div>
            </div>
        </div> <!-- container -->
    </section>
    <!--====== ABOUT THREE PART ENDS ======-->
        <!--====== FOOTER PART START ======-->
    <footer id="footer" class="footer-area">
        <div class="footer-copyright">
            <div class="container">
                <div class="row">
                    <div class="col-lg-12">
                        <div class="copyright text-center">
                                                <p>
                                                <a href="/">首页</a> |
                                                <a href="/free-nodes/">免费节点</a> |
                                                <a href="/paid-subscribe/">推荐机场</a> |
                                                <a href="/news/">新闻资讯</a> |
                                                <a href="/client.htm">客户端</a> |
                                                <a href="/about-us.htm">关于我们</a> |
                        <a href="/disclaimer.htm">免责申明</a> |
                        <a href="/privacy.htm">隐私申明</a> |
                        <a href="/sitemap.xml">网站地图</a>
                    </p>
                            <p class="text">V2ray免费机场 节点订阅中文官网 版权所有 Powered by WordPress</p>
                        </div> <!-- copyright -->
                    </div>
                </div> <!-- row -->
            </div> <!-- container -->
        </div> <!-- footer copyright -->
    </footer>
    <!--====== FOOTER PART ENDS ======-->
    <!--====== BACK TOP TOP PART START ======-->
    <a href="#" class="back-to-top"><i class="lni-chevron-up"></i></a>
    <!--====== BACK TOP TOP PART ENDS ======-->
    <!--====== jquery js ======-->
    <script src="/assets/website/js/frontend/surfboardnode/vendor/modernizr-3.6.0.min.js"></script>
    <script src="/assets/website/js/frontend/surfboardnode/vendor/jquery-1.12.4.min.js"></script>
    <!--====== Bootstrap js ======-->
    <script src="/assets/website/js/frontend/surfboardnode/bootstrap.min.js"></script>
    <script src="/assets/website/js/frontend/surfboardnode/popper.min.js"></script>
    <!--====== Images Loaded js ======-->
    <script src="/assets/website/js/frontend/surfboardnode/imagesloaded.pkgd.min.js"></script>
    <!--====== Scrolling Nav js ======-->
    <script src="/assets/website/js/frontend/surfboardnode/jquery.easing.min.js"></script>
    <script src="/assets/website/js/frontend/surfboardnode/scrolling-nav.js"></script>
    <!--====== Slick js ======-->
    <script src="/assets/website/js/frontend/surfboardnode/slick.min.js"></script>
    <!--====== Main js ======-->
    <script src="/assets/website/js/frontend/surfboardnode/main.js"></script>
    <script src="https://www.freeclashnode.com/assets/js/frontend/invite-url.js"></script>
    <script src="/assets/website/js/frontend/G.js"></script>
</body>

</html>